#include <GameEngine/Graphics/Camera/camera_manipulator.h>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

#ifndef DOXYGEN_IGNORE
#include "camera_manipulator.moc"
#endif

//=================================================================================


//! Default constructor
CameraManipulator::CameraManipulator() : 
	zoom_speed_(1.0f), pan_speed_(1.0f), rotation_speed_(1.0f), QObject() {}

//! Copy constructor
/*!
 @param rhs The camera manipulator to copy
 */
CameraManipulator::CameraManipulator(const CameraManipulator& rhs) :
	camera_(rhs.camera_), bindings_(rhs.bindings_),
	zoom_speed_(rhs.zoom_speed_), pan_speed_(rhs.pan_speed_), rotation_speed_(rhs.rotation_speed_),
	QObject() {}

//! Assignment operator
/*!
 @param rhs The camera manipulator to copy
 @return A reference to this camera manipulator
 */
CameraManipulator& CameraManipulator::operator=(const CameraManipulator& rhs) {
	if ( &rhs != this ) {
		camera_ = rhs.camera_;
		bindings_ = rhs.bindings_;
		zoom_speed_ = rhs.zoom_speed_;
		pan_speed_ = rhs.pan_speed_;
		rotation_speed_ = rhs.rotation_speed_;
	}
	return *this;
}

//! Destructor
CameraManipulator::~CameraManipulator() {}

//! Accessor to the camera associated to this manipulator (read only)
/*!
 @return A constant reference to the camera associated to this manipulator
 */
const Camera& CameraManipulator::camera() const {
	return camera_;
}

//! Accessor to the camera associated to this manipulator (read-write)
/*!
 @return A reference to the camera associated to this manipulator
 */
Camera& CameraManipulator::camera() {
	return camera_;
}

//! Add a binding between the mouse button and the camera effect
/*!
 @param button The mouse button to add
 @param effect The camera effect to add. Effect EffectNone may not be added.
 @param multiple_allowed If set to true, multiple effects could be bound to the same mouse button
 @return True if given binding could be added, false otherwise. Binding may none be added if the
         button already has a binding (multiple_allowed = false) or if the button is already bound
		 to the same effect (multiple_allowed = true)
 */
bool CameraManipulator::add_manipulation(MouseButton button, CameraEffect effect, bool multiple_allowed) {
	// Check button and effect
	if ( effect == EffectNone ) return false;

	// Check other bindings
	if ( multiple_allowed ) {
		if ( find_manipulation(button, effect) == true ) return false;
	}
	else {
		if ( find_manipulation(button).empty() == false ) return false;
	}

	// Add the binding
	bindings_.append( QPair<MouseButton, CameraEffect>(button, effect) );
	return true;
}

//! Remove the given binding
/*!
 @param button The mouse button to look for
 @param effect The camera effect to look for. If effect is EffectNone, all effects associated to the
               given mouse button will be removed
 @return The number of effects that were removed
 */
int CameraManipulator::remove_manipulation(MouseButton button, CameraEffect effect) {
	// Scan all bindings
	int res = 0;
	if ( effect == EffectNone ) {
		res += bindings_.removeAll( QPair<MouseButton, CameraEffect>(button, EffectZoom) );
		res += bindings_.removeAll( QPair<MouseButton, CameraEffect>(button, EffectPan) );
		res += bindings_.removeAll( QPair<MouseButton, CameraEffect>(button, EffectRotate) );
	}
	else res += bindings_.removeAll( QPair<MouseButton, CameraEffect>(button, effect) );

	// Return number of removed bindings
	return res;
}

//! Find the list of effects associated to the given mouse button
/*!
 @param button The mouse button to look for
 @return The list of camera effects associated to the given mouse button
 */
QList<CameraManipulator::CameraEffect> CameraManipulator::find_manipulation(MouseButton button) const {
	QList<CameraEffect> res;

	// Scan all bindings
	for ( QList< QPair<MouseButton, CameraEffect> >::const_iterator it = bindings_.begin(); it != bindings_.end(); ++it ) {
		if ( it->first != button ) continue;
		res.append(it->second);
	}

	// Return all effects
	return res;
}

//! Find if given button has given effect on the camera
/*!
 @param button The mouse button to look for
 @param effect The camera effect to look for
 @return True if such a binding was found, false otherwise
 */
bool CameraManipulator::find_manipulation(MouseButton button, CameraEffect effect) const {
	// Scan all bindings
	for ( QList< QPair<MouseButton, CameraEffect> >::const_iterator it = bindings_.begin(); it != bindings_.end(); ++it ) {
		if ( it->first != button ) continue;
		if ( it->second != effect ) continue;
		return true;
	}

	// No such manipulation was found
	return false;
}

//! Accessor to the speed at which camera is zoomed (read only)
/*!
 @return The speed at which camera is zoomed
 */
float CameraManipulator::zoom_speed() const {
	return zoom_speed_;
}

//! Accessor to the speed at which camera is zoomed (read-write)
/*!
 @return A reference to the speed at which camera is zoomed
 */
float& CameraManipulator::zoom_speed() {
	return zoom_speed_;
}

//! Accessor to the speed at which camera is panned (read only)
/*!
 @return The speed at which camera is panned
 */
float CameraManipulator::pan_speed() const {
	return pan_speed_;
}

//! Accessor to the speed at which camera is panned (read-write)
/*!
 @return A reference to the speed at which camera is panned
 */
float& CameraManipulator::pan_speed() {
	return pan_speed_;
}

//! Accessor to the speed at which camera is rotated (read only)
/*!
 @return The speed at which camera is rotated
 */
float CameraManipulator::rotation_speed() const {
	return rotation_speed_;
}

//! Accessor to the speed at which camera is rotated (read-write)
/*!
 @return A reference to the speed at which camera is rotated
 */
float& CameraManipulator::rotation_speed() {
	return rotation_speed_;
}

//! Update the camera manipulator
/*!
 @return Default implementation always returns true
 */
bool CameraManipulator::update() {
	return true;
}


#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif
